home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 16 code / Simple Drag / SimpleDrag.c next >
Encoding:
C/C++ Source or Header  |  1993-10-08  |  27.6 KB  |  1,099 lines  |  [TEXT/KAHL]

  1. /*
  2.  *  SimpleDrag.c     Drag Manager sample program
  3.  *
  4.  *  SimpleDrag lets the user make one or more picture windows
  5.  *  Pictures can be dragged among (amongst?) windows and from
  6.  *  PICT files
  7.  *
  8.  *  All code using the Drag Manager is at the beginning of this
  9.  *  file (darn near, anyway)
  10.  *
  11.  *  v 1.0d4  8/93 Greg Robbins
  12.  *
  13.  *    93/9 dj final touch up to match final interfaces:
  14.  *        Changed DragGetXXX -> GetDragXXX
  15.  *        Changed DragFlags -> DragAttributes and some flag names changed
  16.  *        Eliminated last parameter to GetFlavorData to match prototype
  17.  *        
  18.  */
  19.  
  20. #include "SimpleDrag.h"     // nothing interesting is in SimpleDrag.h
  21.  
  22. // menu constants
  23. #define kAppleMenuID 1
  24. #define kAboutMenuItem 1
  25. #define kFileMenuID 2
  26. #define kNewMenuItem 1
  27. #define kOpenMenuItem 2
  28. #define kCloseMenuItem 3
  29. #define kQuitMenuItem 5
  30.  
  31.  
  32. // general global variables
  33.  
  34. Boolean            gQuitFlag;    // true when the app should exit the main event loop
  35.  
  36. Boolean            gHasColorQuickdrawFlag; // Gestalt info
  37.  
  38. MenuHandle        gAppleMenuHandle, gFileMenuHandle;
  39. unsigned short    gWindowCounter;
  40.  
  41. // data structure for each window
  42.  
  43. typedef struct WindowData {
  44.     PicHandle windowPic;
  45. };
  46. typedef struct WindowData WindowData, *WindowDataPtr, **WindowDataHandle;
  47.  
  48.  
  49. // global data for my Drag Manager handlers
  50.  
  51. typedef struct DragHandlerGlobals {
  52.     Boolean acceptableDragFlag;
  53.     Boolean windowIsHilightedFlag;
  54. };
  55. typedef struct DragHandlerGlobals
  56.     DragHandlerGlobals, *DragHandlerGlobalsPtr;
  57.  
  58. DragHandlerGlobals gDragHandlerGlobals;
  59.  
  60.  
  61. /***************************************************
  62.  *    Drag support routines
  63.  *
  64.  *    These are the installed drag handler routines
  65.  *    and supporting functions
  66.  ***************************************************/
  67.  
  68. // InstallDragHandlers attaches my tracking and receive handlers to
  69. // one of the application's windows
  70.  
  71. OSErr InstallDragHandlers(WindowPtr theWindow)
  72. {
  73.     // install our tracking and receive handlers for the window
  74.     
  75.     OSErr retCode;
  76.     
  77.     retCode = InstallTrackingHandler(MyTrackingHandler, theWindow, nil);
  78.  
  79.     if (retCode == noErr) {
  80.         retCode = InstallReceiveHandler(MyReceiveHandler, theWindow, nil);
  81.         if (retCode != noErr)
  82.             (void) RemoveTrackingHandler(MyTrackingHandler, theWindow);
  83.     }
  84.     
  85.     return retCode;
  86. }
  87.  
  88.  
  89. // RemoveDragHandlers removes my tracking and receive handlers from
  90. // one of the application's windows (prior to the window's disposal,
  91. // presumably)
  92.  
  93. void RemoveDragHandlers(WindowPtr theWindow)
  94. {
  95.     RemoveReceiveHandler(MyReceiveHandler, theWindow);
  96.     RemoveTrackingHandler(MyTrackingHandler, theWindow);
  97. }
  98.  
  99.  
  100. // MouseInContentRgn returns true if the current mouse is in the content
  101. // area of the window (but not necessarily in the visible rgn)
  102.  
  103. Boolean MouseIsInContentRgn(DragReference theDrag, WindowPtr theWindow)
  104. {
  105.     Point mousePt;
  106.     
  107.     (void) GetDragMouse(theDrag, &mousePt, nil);
  108.     return PtInRgn(mousePt, ((WindowPeek) theWindow)->contRgn);
  109. }
  110.  
  111.  
  112. // DragItemsAreAcceptable returns true if the contents (data) of
  113. // the drag are acceptable by a window of this application
  114. //
  115. // DragItemsAreAcceptable is called by the tracking and 
  116. // receive handlers
  117.  
  118. Boolean DragItemsAreAcceptable(DragReference theDrag)
  119. {
  120.     OSErr            retCode;
  121.     unsigned short    totalItems;
  122.     ItemReference    itemRef;
  123.     Boolean            acceptableFlag;
  124.     HFSFlavor         currHFSFlavor;
  125.     Size            flavorDataSize;
  126.     FlavorFlags        currFlavorFlags;
  127.     
  128.     acceptableFlag = false;
  129.  
  130.     // this app can only accept the drag of a single item
  131.     retCode = CountDragItems(theDrag, &totalItems);
  132.     if (retCode == noErr && totalItems == 1) {
  133.     
  134.         // get the reference number of the dragged item
  135.         retCode = GetDragItemReferenceNumber(theDrag, 1, &itemRef);
  136.         if (retCode == noErr) {
  137.             
  138.             // use GetFlavorFlags to check on flavor existence of PICT data
  139.             // without forcing translation
  140.             
  141.             retCode = GetFlavorFlags (theDrag, itemRef, 'PICT', &currFlavorFlags);
  142.             if (retCode == noErr)
  143.                 acceptableFlag = true;
  144.  
  145.             else {
  146.             
  147.                 // check if the item is a file spec for a PICT file
  148.                 flavorDataSize = sizeof(HFSFlavor);
  149.                 retCode = GetFlavorData(theDrag, itemRef, flavorTypeHFS, &currHFSFlavor,
  150.                     &flavorDataSize, 0);
  151.                 
  152.                 if (retCode == noErr && currHFSFlavor.fileType == 'PICT') 
  153.                     acceptableFlag = true;
  154.             }
  155.         }
  156.     }
  157.     return acceptableFlag;
  158. }
  159.  
  160.  
  161. // DragIsNotInSourceWindow returns true if the drag in progress
  162. // is not in the same window it originated in
  163. //
  164. // DragIsNotInSourceWindow is called by the tracking and receive handlers
  165. //
  166. // Note that, if this application allowed items to be dragged within
  167. // its windows, this function would not be appropriate.
  168. // Instead, hilighting would probably occur in the source window
  169. // when the dragHasLeftSourceWindow flag is set, and the receive
  170. // handler wouldn't check this at all
  171.  
  172. Boolean DragIsNotInSourceWindow(DragReference theDrag)
  173. {
  174.     DragAttributes currDragFlags;
  175.     
  176.     (void) GetDragAttributes(theDrag, &currDragFlags);
  177.     return ((currDragFlags & dragInsideSenderWindow) == 0);
  178. }
  179.  
  180.  
  181. // MyTrackingHandler is called by the drag manager whenever a drag is
  182. // over one of the application's windows
  183. //
  184. // upon entry, the current port has been set to theWindow by the Drag Manager
  185.  
  186. pascal OSErr MyTrackingHandler(DragTrackingMessage theMessage, WindowPtr theWindow,
  187.     void *handlerRefCon, DragReference theDrag)
  188. {
  189. #pragma unused (handlerRefCon)
  190.  
  191.     RgnHandle    tempRgn;
  192.     Boolean        mouseInContentFlag;
  193.     OSErr        retCode;
  194.     
  195.     retCode = noErr;
  196.     
  197.     switch (theMessage) {
  198.     
  199.         case dragTrackingEnterHandler:
  200.             
  201.             // determine if the items are acceptable and store
  202.             // that flag in the globals, plus reset the
  203.             // hilighted global flag
  204.             
  205.             gDragHandlerGlobals.acceptableDragFlag = 
  206.                 DragItemsAreAcceptable(theDrag);
  207.             gDragHandlerGlobals.windowIsHilightedFlag = false;
  208.             
  209.             // let the drag manager know if we can't accept this drag
  210.             if (!gDragHandlerGlobals.acceptableDragFlag)
  211.                 retCode = dragNotAcceptedErr;
  212.             break;
  213.             
  214.         case dragTrackingEnterWindow: 
  215.         case dragTrackingInWindow:
  216.         case dragTrackingLeaveWindow:
  217.             
  218.             // highlighting of the window during a drag is done
  219.             // here.  Do it only if we can accept these items
  220.             // and we're not in the source window...
  221.             
  222.             if (gDragHandlerGlobals.acceptableDragFlag &&
  223.                 DragIsNotInSourceWindow(theDrag)) {
  224.                 
  225.                 // unless the mouse is leaving the visible area of the
  226.                 // window, check if it's in the window's content region
  227.                 
  228.                 if (theMessage == dragTrackingLeaveWindow)
  229.                     mouseInContentFlag = false;
  230.  
  231.                 else
  232.                     mouseInContentFlag = MouseIsInContentRgn(theDrag, theWindow);
  233.                 
  234.                 // if the mouse is in the content area and the window
  235.                 // is not yet hilighted, then do the hilighting
  236.                 
  237.                 if (mouseInContentFlag &&
  238.                     !gDragHandlerGlobals.windowIsHilightedFlag) {
  239.                     
  240.                     // set the proper clip
  241.                     ClipRect(&theWindow->portRect);
  242.                     
  243.                     // make a region bordering the window content
  244.                     tempRgn = NewRgn();
  245.                     RectRgn(tempRgn, &theWindow->portRect);
  246.                     
  247.                     // draw the hilight
  248.                     if (ShowDragHilite(theDrag, tempRgn, true) == noErr)
  249.                     
  250.                         // remember that hilighting is now on
  251.                         gDragHandlerGlobals.windowIsHilightedFlag = true;                    
  252.                     
  253.                     // free up the region
  254.                     DisposeRgn(tempRgn);
  255.                 }
  256.                 
  257.                 // else if the mouse is not in the content region
  258.                 // and the window is hilighted, erase the hilight
  259.                 
  260.                 else if (!mouseInContentFlag &&
  261.                     gDragHandlerGlobals.windowIsHilightedFlag) {
  262.                     
  263.                     // set the proper clip
  264.                     ClipRect(&theWindow->portRect);
  265.                     
  266.                     // erase the hilight and restore the port
  267.                     if (HideDragHilite(theDrag) == noErr)
  268.                     
  269.                         // remember that hilighting is now off
  270.                         gDragHandlerGlobals.windowIsHilightedFlag = false;
  271.                 }
  272.             }
  273.             break;
  274.  
  275.         // do nothing for the leaveHandler message
  276.         case dragTrackingLeaveHandler:
  277.             break;
  278.         
  279.         // let the drag manager know if we didn't recognize the message
  280.         default:
  281.             retCode = paramErr;
  282.     }
  283.     
  284.     return retCode;
  285. }
  286.  
  287.  
  288. // MyReceiveHandler is called by the drag manager whenever a drag is
  289. // ends on one of the application's windows
  290.  
  291. pascal OSErr MyReceiveHandler(WindowPtr theWindow, void *handlerRefCon, 
  292.         DragReference theDrag)
  293. {
  294. #pragma unused (handlerRefCon)
  295.     ItemReference    itemRef;
  296.     Size                dataSize;
  297.     Handle            tempHandle;
  298.     HFSFlavor        theHFSFlavor;
  299.     Boolean            dataObtainedFlag;
  300.     OSErr                retCode;
  301.     
  302.     dataObtainedFlag = false;
  303.     if (!DragItemsAreAcceptable(theDrag) ||
  304.             !MouseIsInContentRgn(theDrag, theWindow) ||
  305.             !DragIsNotInSourceWindow(theDrag)) 
  306.         return dragNotAcceptedErr;
  307.  
  308.     // There is only one item, so get its reference number.
  309.     retCode = GetDragItemReferenceNumber(theDrag, 1, &itemRef);
  310.     if (retCode != noErr)
  311.         return retCode;
  312.     
  313.     // PICT data is preferred, so get it if it's available.
  314.     retCode = GetFlavorDataSize(theDrag, itemRef, 'PICT', &dataSize);
  315.     if (retCode == noErr) {
  316.         tempHandle = TempNewHandle(dataSize, &retCode);
  317.         if (tempHandle == nil) {
  318.             tempHandle = NewHandle(dataSize);
  319.         }
  320.         if (tempHandle != nil) {
  321.             HLock(tempHandle);
  322.             retCode = GetFlavorData(theDrag, itemRef, 'PICT', *tempHandle, 
  323.                                                                     &dataSize, 0);
  324.             if (retCode == noErr) {
  325.                 retCode = SetWindowPicture(theWindow, (PicHandle) tempHandle);
  326.                 if (retCode == noErr)
  327.                     dataObtainedFlag = true;
  328.             }
  329.             DisposeHandle(tempHandle);
  330.         }
  331.     }
  332.  
  333.     if (!dataObtainedFlag) {
  334.         // Couldn't get PICT data so try to get HFS-flavor data.
  335.         dataSize = sizeof(HFSFlavor);
  336.         retCode = GetFlavorData(theDrag, itemRef, flavorTypeHFS, 
  337.                                                 &theHFSFlavor, &dataSize, 0);
  338.         if (retCode == noErr && theHFSFlavor.fileType == 'PICT') {
  339.             retCode = SetWindowPictureFromFile(&theHFSFlavor.fileSpec, 
  340.                                                                                     theWindow);
  341.         }
  342.     }
  343.  
  344.     if (retCode != noErr)
  345.         (void) ReportErrorInWindow(nil, 
  346.                                 "\pCannot display received picture. ", retCode);
  347.     return retCode;
  348. }
  349.  
  350.  
  351. // OutlineRegion changes a region into a tracing of its border
  352. // which is appropriate for normal dragging
  353. //
  354. // OutlineRegion is called by DoWindowContentDrag
  355.  
  356. void OutlineRegion(RgnHandle theRgn)
  357. {
  358.     RgnHandle tempRgn;
  359.     
  360.     tempRgn = NewRgn();
  361.     CopyRgn(theRgn, tempRgn);
  362.     InsetRgn(tempRgn, 1, 1);
  363.     DiffRgn(theRgn, tempRgn, theRgn);
  364.     DisposeRgn(tempRgn);
  365. }
  366.  
  367.  
  368. // DoWindowContentDrag is called by the application whenever a drag
  369. // begins on one of the app's windows
  370.  
  371. OSErr DoWindowContentDrag(WindowPtr theWindow, EventRecord *theEvent)
  372. {
  373.     OSErr            retCode;
  374.     DragReference    theDrag;
  375.     Rect            dragBounds;
  376.     RgnHandle        dragRgn;
  377.     ItemReference    theItem;
  378.     PicHandle        windowPicHandle;
  379.     short            mouseUpModifiers;
  380.     DragAttributes    currDragFlags;
  381.     
  382.     // create a new drag
  383.     retCode = NewDrag(&theDrag);
  384.     if (retCode != noErr) goto Bail;
  385.     
  386.     // use the window ptr as item reference for the heck of it
  387.     theItem = (ItemReference) theWindow;
  388.     
  389.     // add the picture data to the drag
  390.     windowPicHandle = GetWindowPicture(theWindow);
  391.     HLock((Handle) windowPicHandle);
  392.     
  393.     retCode = AddDragItemFlavor(theDrag, theItem, 'PICT', 
  394.         (Ptr) *windowPicHandle,
  395.         GetHandleSize((Handle) windowPicHandle), 0);
  396.         
  397.     HUnlock((Handle) windowPicHandle);
  398.     if (retCode != noErr) goto DisposeDragAndBail;
  399.     
  400.     // generate the bounds and region for the drag using the window's
  401.     // content rectangle
  402.     dragBounds = (**((WindowPeek) theWindow)->contRgn).rgnBBox;
  403.     retCode = SetDragItemBounds(theDrag, theItem, &dragBounds);
  404.     if (retCode != noErr) goto DisposeDragAndBail;
  405.     
  406.     dragRgn = NewRgn();
  407.     RectRgn(dragRgn, &dragBounds);
  408.     OutlineRegion(dragRgn);
  409.     
  410.     // do the drag and clean up
  411.     retCode = TrackDrag(theDrag, theEvent, dragRgn);
  412.     
  413.     DisposeRgn(dragRgn);
  414.     
  415.     if (retCode == noErr) {
  416.         
  417.         // the drag was successful
  418.         
  419.         // if the option key was not pressed at mouseUp and the drag ended
  420.         // in the source application then this is a move operation and we
  421.         // have to clear the source window
  422.         
  423.         (void) GetDragModifiers(theDrag, nil, nil, &mouseUpModifiers);
  424.                 
  425.         (void) GetDragAttributes(theDrag, &currDragFlags);
  426.     
  427.         if ((mouseUpModifiers & optionKey) == 0 &&
  428.             (currDragFlags & dragInsideSenderApplication) != 0)
  429.             
  430.             (void) SetEmptyWindowPicture(theWindow);
  431.     }
  432.     
  433. DisposeDragAndBail:
  434.     DisposeDrag(theDrag);
  435.     
  436. Bail:
  437.     return retCode;
  438. }
  439.  
  440.  
  441.  
  442. /***************************************************
  443.  *  Apple Event routines
  444.  *
  445.  *  These routines handle the required Apple events
  446.  ***************************************************/
  447.  
  448. pascal OSErr DoAEOpenApplication(AppleEvent * theAppleEvent,
  449.                                  AppleEvent * replyAppleEvent, 
  450.                                  long refCon)
  451. {
  452. #pragma unused (theAppleEvent, replyAppleEvent, refCon)
  453.  
  454.     // make an empty window
  455.     (void) DoNewWindow();
  456.     return noErr;
  457. }
  458.  
  459. pascal OSErr DoAEOpenDocuments(AppleEvent * theAppleEvent,
  460.                                AppleEvent * replyAppleEvent, 
  461.                                long refCon)
  462. {
  463. #pragma unused (replyAppleEvent, refCon)
  464.     OSErr        retCode;
  465.     FSSpec        currSpec;
  466.     AEDescList    docDescList;
  467.     long        itemCount, index;
  468.     AEKeyword    keyword;
  469.     DescType    typeCode;
  470.     Size        actualSize;
  471.     
  472.     // retrieve all documents from the Apple event and open them
  473.     retCode = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList,
  474.         &docDescList);
  475.     if (retCode != noErr) goto Bail;
  476.     
  477.     retCode = AECountItems(&docDescList, &itemCount);
  478.     if (retCode != noErr) goto DisposeDocDescListAndBail;
  479.  
  480.      for (index = 1; index <= itemCount; index++) {
  481.          retCode = AEGetNthPtr(&docDescList, index, typeFSS,
  482.              &keyword, &typeCode, (Ptr) &currSpec, sizeof(FSSpec),
  483.              &actualSize);
  484.          if (retCode != noErr) goto DisposeDocDescListAndBail;
  485.          
  486.          (void) OpenPictureInNewWindow(&currSpec);
  487.      }
  488.      
  489.  DisposeDocDescListAndBail:
  490.      (void) AEDisposeDesc(&docDescList);
  491.      
  492.  Bail:
  493.      if (retCode == noErr) return noErr;
  494.      else return errAEEventNotHandled;
  495. }
  496.  
  497. pascal OSErr DoAEQuitApplication(AppleEvent * theAppleEvent,
  498.                                  AppleEvent * replyAppleEvent, 
  499.                                  long refCon)
  500. {
  501. #pragma unused (theAppleEvent, replyAppleEvent, refCon)
  502.  
  503.     DoQuit();     // set the quit flag (doesn't immediately quit)
  504.     return noErr;
  505. }
  506.  
  507. void DoHighLevelEvent(EventRecord * theEventRecPtr)
  508. // high-level event dispatching
  509. {
  510.     (void) AEProcessAppleEvent(theEventRecPtr);
  511. }
  512.  
  513. OSErr InstallAppleEventHandlers()
  514. {
  515.     OSErr retCode;
  516.     
  517.     retCode = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
  518.                 (EventHandlerProcPtr) DoAEOpenApplication, 0, false);
  519.                                     
  520.     if (retCode == noErr)
  521.         retCode = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
  522.                 (EventHandlerProcPtr) DoAEOpenDocuments, 0, false);
  523.  
  524.     if (retCode == noErr)
  525.         retCode = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
  526.                 (EventHandlerProcPtr) DoAEQuitApplication, 0, false);
  527.     
  528.     return retCode;
  529. }
  530.  
  531.  
  532. /***************************************************
  533.  *   general application utility routines
  534.  ***************************************************/
  535.  
  536. // ConcatPascalStrings concatenates s2 to the end of s1
  537. void ConcatPascalStrings(StringPtr s1, StringPtr s2)
  538. {
  539.     short s1Length, s2Length;
  540.     
  541.     s1Length = s1[0];
  542.     s2Length = s2[0];
  543.     BlockMove(&s2[1], &s1[s1Length+1], s2Length);
  544.     s1[0] = s1Length + s2Length;
  545. }
  546.  
  547. // GetApplicationName uses the Process Manager to find
  548. // the current app's name
  549. OSErr GetApplicationName(StringPtr appNameString)
  550. {
  551.     ProcessInfoRec        appPIR;
  552.     ProcessSerialNumber    appPSN;
  553.     
  554.     appPSN.lowLongOfPSN = kCurrentProcess;
  555.     appPSN.highLongOfPSN = 0;
  556.     
  557.     appPIR.processInfoLength = sizeof(ProcessInfoRec);
  558.     appPIR.processAppSpec = nil;
  559.     appPIR.processName = appNameString;
  560.     
  561.     return GetProcessInformation(&appPSN, &appPIR);
  562. }
  563.  
  564.  
  565. /***************************************************
  566.  *  routines for dealing with the window pictures
  567.  ***************************************************/
  568.  
  569. // GetWindowPicture returns the handle of a window's picture
  570.  
  571. PicHandle GetWindowPicture(WindowPtr theWindow)
  572. {
  573.     WindowDataHandle    windowData;
  574.  
  575.     windowData = (WindowDataHandle) GetWRefCon(theWindow);
  576.     return (**windowData).windowPic;
  577. }
  578.  
  579.  
  580. // SetWindowPicture copies thePicture into the existing windowPic
  581. // handle of theWindow's data.  If the copy is successful,
  582. // the window is invalidated to force it to be redrawn
  583.  
  584. OSErr SetWindowPicture(WindowPtr theWindow, PicHandle newPicture)
  585. {
  586.     PicHandle    windowPic;
  587.     Size        picSize;
  588.     GrafPtr        savePort;
  589.     OSErr        retCode;
  590.     
  591.     // get the old picture handle
  592.     windowPic = GetWindowPicture(theWindow);
  593.     
  594.     // resize the old handle to hold the new picture
  595.     picSize = GetHandleSize((Handle) newPicture);
  596.     SetHandleSize((Handle) windowPic, picSize);
  597.     retCode = MemError();
  598.     if (retCode == noErr) {
  599.     
  600.         // copy the picture and invalidate the window
  601.         BlockMove(*newPicture, *windowPic, picSize);
  602.         
  603.         GetPort(&savePort);
  604.         SetPort(theWindow);
  605.         InvalRect(&theWindow->portRect);
  606.         SetPort(savePort);
  607.     }
  608.     return retCode;
  609. }
  610.  
  611.  
  612. // SetEmptyWindowPicture replaces the existing picture handle
  613. // for a window with a new, empty one
  614.  
  615. OSErr SetEmptyWindowPicture(WindowPtr theWindow)
  616. {
  617.     PicHandle emptyPicture;
  618.     
  619.     // make a new, empty picture handle
  620.     emptyPicture = (PicHandle) NewHandleClear(sizeof(Picture));
  621.     
  622.     if (emptyPicture != nil)
  623.     
  624.         // replace the window's picture with the empty picture
  625.         return SetWindowPicture(theWindow, emptyPicture);
  626.         
  627.     else
  628.         return MemError();
  629. }
  630.  
  631.  
  632. // SetWindowPictureFromFile reads the PICT file specified by pictSpec
  633. // and replaces the window's picture with the new one
  634.  
  635. OSErr SetWindowPictureFromFile(FSSpecPtr pictSpec, WindowPtr theWindow)
  636. {
  637.     OSErr        retCode;
  638.     short        pictRefNum;
  639.     long        fileLength;
  640.     Size        pictSize;
  641.     Handle        tempHandle;
  642.     
  643.     pictRefNum = 0;
  644.     tempHandle = nil;
  645.     
  646.     // open the file and find its size
  647.     retCode = FSpOpenDF(pictSpec, fsRdPerm, &pictRefNum);
  648.     if (retCode != noErr) goto Bail;
  649.     
  650.     retCode = GetEOF(pictRefNum, &fileLength);
  651.     if (retCode != noErr) goto Bail;
  652.     
  653.     // skip over 512-byte pict file header
  654.     retCode = SetFPos(pictRefNum, fsFromMark, 512);
  655.     if (retCode != noErr) goto Bail;
  656.  
  657.     pictSize = fileLength - 512;
  658.     
  659.     // allocate a buffer for the file's picture data,
  660.     // in temp memory if it is available
  661.     tempHandle = TempNewHandle(pictSize, &retCode);
  662.     if (tempHandle == nil) {
  663.         tempHandle = NewHandle(pictSize);
  664.         retCode = MemError();
  665.     }
  666.     if (tempHandle == nil) goto Bail;
  667.     
  668.     // read in the picture data
  669.     HLock(tempHandle);
  670.     retCode = FSRead(pictRefNum, &pictSize, *tempHandle);
  671.     HUnlock(tempHandle);
  672.     if (retCode != noErr) goto Bail;
  673.     
  674.     // replace the window's picture with the new data
  675.     retCode = SetWindowPicture(theWindow, (PicHandle) tempHandle);
  676.     
  677. Bail:
  678.     if (pictRefNum != 0) FSClose(pictRefNum);
  679.     if (tempHandle != nil) DisposeHandle(tempHandle);
  680.     
  681.     return retCode;
  682. }
  683.  
  684.  
  685. // OpenPictureInNewWindow creates a new window and sets its
  686. // picture to be the one contained in the specified PICT file
  687. // If the file cannot be read, an error message is displayed
  688. // in the window instead
  689.  
  690. WindowPtr OpenPictureInNewWindow(FSSpecPtr fileSpec)
  691. {
  692.     WindowPtr    theWindow;
  693.     OSErr                retCode;
  694.     
  695.     theWindow = DoNewWindow();
  696.     if (theWindow != nil) {
  697.     
  698.         retCode = SetWindowPictureFromFile(fileSpec, theWindow);
  699.         if (retCode != noErr) {
  700.             (void) ReportErrorInWindow(theWindow,
  701.                 "\pCannot display opened picture. ", retCode);
  702.         }
  703.     }
  704.     return theWindow;
  705. }
  706.  
  707.  
  708. // DrawWindow draws a window's picture in the window
  709.  
  710. void DrawWindow(WindowPtr theWindow)
  711. {
  712.     PicHandle    windowPic;
  713.     Rect        tempRect;
  714.     
  715.     windowPic = GetWindowPicture(theWindow);
  716.     
  717.     if (windowPic != nil) {
  718.         tempRect = (**windowPic).picFrame;
  719.         DrawPicture(windowPic, &tempRect);
  720.     }
  721. }
  722.  
  723. // ReportErrorInWindow draws a string and a number in the specified window
  724. // if theWindow is nil, a new window is created and its WindowPtr is returned
  725.  
  726. WindowPtr ReportErrorInWindow(WindowPtr theWindow, StringPtr theString,
  727.     OSErr errNum)
  728. {
  729.     Str255     copyStr;
  730.     Str15    numStr;
  731.     
  732.     BlockMove(theString, copyStr, theString[0]+1);
  733.     NumToString(errNum, numStr);
  734.     ConcatPascalStrings(copyStr, numStr);
  735.     return ReportStringInWindow(theWindow, copyStr);
  736. }
  737.  
  738. // ReportStringInWindow draws a string in the specified window
  739. // if theWindow is nil, a new window is created and its WindowPtr is returned
  740.  
  741. WindowPtr ReportStringInWindow(WindowPtr theWindow, StringPtr theString)
  742. {
  743.     PicHandle    tempPic;
  744.     GrafPtr        savePort;
  745.     
  746.     if (theWindow == nil)
  747.         theWindow = DoNewWindow();
  748.     
  749.     if (theWindow != nil) {
  750.         GetPort(&savePort);
  751.         SetPort(theWindow);
  752.         
  753.         tempPic = OpenPicture(&theWindow->portRect);
  754.         MoveTo(20, theWindow->portRect.bottom / 3);
  755.         TextFont(systemFont);
  756.         DrawString(theString);
  757.         ClosePicture();
  758.         SetWindowPicture(theWindow, tempPic);
  759.         KillPicture(tempPic);    
  760.         
  761.         SetPort(savePort);    
  762.     }
  763.     return theWindow;
  764. }
  765.  
  766.  
  767. /***************************************************
  768.  *  typical Mac toolbox routines
  769.  ***************************************************/
  770.  
  771. // CreateMenus makes menus the old-fashioned way
  772. void CreateMenus()
  773. {
  774.     // create Apple menu
  775.     gAppleMenuHandle = NewMenu(kAppleMenuID, "\p\024");
  776.     AppendMenu(gAppleMenuHandle, "\pAbout SimpleDrag...;(-");
  777.     AddResMenu(gAppleMenuHandle, 'DRVR');
  778.     InsertMenu(gAppleMenuHandle, 0);
  779.  
  780.     // create File menu
  781.     gFileMenuHandle = NewMenu(kFileMenuID, "\pFile");
  782.     AppendMenu(gFileMenuHandle, "\pNew/N;Open.../O;Close/W;(-;Quit/Q");
  783.     InsertMenu(gFileMenuHandle, 0);
  784.     
  785.     DrawMenuBar();
  786. }
  787.  
  788. // DoOpen displays a standard file dialog and, if the user selects
  789. // a PICT file, opens the file in a new window
  790. void DoOpen()
  791. {
  792.     StandardFileReply    fileSFR;
  793.     SFTypeList            sfTypes;
  794.     
  795.     sfTypes[0] = 'PICT';
  796.     
  797.     StandardGetFile(nil, 1, sfTypes, &fileSFR);
  798.     if (fileSFR.sfGood)
  799.         (void) OpenPictureInNewWindow(&fileSFR.sfFile);
  800. }
  801.  
  802.  
  803. // DoNewWindow creates a new window with an empty picture
  804. // and returns the window's WindowPtr
  805.  
  806. WindowPtr DoNewWindow()
  807. {
  808.     WindowPtr            newWindow;
  809.     Str63                windowNameStr;
  810.     Str15                numStr;
  811.     WindowDataHandle    newWindowDataHandle;
  812.     Rect                windowRect;
  813.     
  814.     // one more window in the world
  815.     gWindowCounter++;
  816.     
  817.     // find a size and place for the new window
  818.     windowRect = qd.screenBits.bounds;
  819.     SetRect(&windowRect, windowRect.left, windowRect.top + 40,
  820.         windowRect.right / 2, windowRect.bottom / 2);
  821.     OffsetRect(&windowRect, 20 * (gWindowCounter % 10), 20 * (gWindowCounter % 10));
  822.  
  823.     // make a name for the window by concatenating gWindowCounter to the app name
  824.     NumToString(gWindowCounter, numStr);
  825.     if (GetApplicationName(windowNameStr) != noErr)
  826.         *windowNameStr = 0;
  827.     ConcatPascalStrings(windowNameStr, "\p ");
  828.     ConcatPascalStrings(windowNameStr, numStr);
  829.     
  830.     // open the window
  831.     if (gHasColorQuickdrawFlag)
  832.         newWindow = NewCWindow(nil, &windowRect, windowNameStr, true,
  833.             documentProc, (WindowPtr) -1, true, 0);
  834.     else
  835.         newWindow = NewWindow(nil, &windowRect, windowNameStr, true,
  836.             documentProc, (WindowPtr) -1, true, 0);
  837.     
  838.     if (newWindow == nil) goto Bail;
  839.     
  840.     SetPort(newWindow);
  841.     ClipRect(&newWindow->portRect);
  842.     
  843.     // attach my drag handlers to this window
  844.     
  845.     if (InstallDragHandlers(newWindow) != noErr)
  846.         goto DisposeWindowAndBail;
  847.     
  848.     // allocate a data structure and a blank picture for the window
  849.     
  850.     newWindowDataHandle = (WindowDataHandle) NewHandle(sizeof(WindowData));
  851.     if (newWindowDataHandle == nil) goto RemoveHandlersAndBail;
  852.  
  853.     (**newWindowDataHandle).windowPic = 
  854.         (PicHandle) NewHandleClear(sizeof(Picture));
  855.     if ((**newWindowDataHandle).windowPic == nil) {
  856.         DisposeHandle((Handle) newWindowDataHandle);
  857.         goto RemoveHandlersAndBail;
  858.     }
  859.     
  860.     SetWRefCon(newWindow, (long) newWindowDataHandle);
  861.         
  862.     return newWindow;
  863.  
  864. RemoveHandlersAndBail:
  865.     RemoveDragHandlers(newWindow);
  866.     
  867. DisposeWindowAndBail:
  868.     DisposeWindow(newWindow);
  869.     
  870. Bail:
  871.     gWindowCounter--;
  872.     return nil;
  873.     
  874. }
  875.  
  876. // DoCloseWindow disposes of a window and does all necessary clean-up
  877. void DoCloseWindow(WindowPtr theWindow)
  878. {
  879.     if (theWindow != nil) {
  880.         KillPicture(GetWindowPicture(theWindow));
  881.         DisposeHandle((Handle) GetWRefCon(theWindow));
  882.         RemoveDragHandlers(theWindow);
  883.         DisposeWindow(theWindow);
  884.     }
  885. }
  886.  
  887. // DoQuit closes all open windows and sets the global quit flag
  888. void DoQuit()
  889. {
  890.     while (FrontWindow() != nil)
  891.         DoCloseWindow(FrontWindow());
  892.     gQuitFlag = true;
  893. }
  894.  
  895. // DoAboutWindow just raises a new window and displays my name
  896. WindowPtr DoAboutWindow()
  897. {
  898.     return ReportStringInWindow(nil, "\pSimpleDrag by Greg Robbins   July 1993");    
  899. }
  900.  
  901. // DoMenuCommand handles user menu selections
  902. void DoMenuCommand(long menuVal)
  903. {
  904.     short        theItem, theMenu;
  905.     Str255        deskAccessoryName;
  906.     
  907.     theItem = LoWord(menuVal);
  908.     theMenu = HiWord(menuVal);
  909.  
  910.     switch (theMenu) {
  911.  
  912.         case kAppleMenuID:
  913.             if (theItem == kAboutMenuItem)
  914.                 (void) DoAboutWindow();
  915.                 
  916.             else {
  917.                 GetItem(gAppleMenuHandle, theItem, deskAccessoryName);
  918.                 (void) OpenDeskAcc(deskAccessoryName);
  919.             }
  920.             break;
  921.  
  922.         case kFileMenuID:
  923.  
  924.             if (theItem == kNewMenuItem)
  925.                 (void) DoNewWindow();
  926.  
  927.             else if (theItem == kOpenMenuItem)
  928.                 DoOpen();
  929.             
  930.             else if (theItem == kCloseMenuItem)
  931.                 DoCloseWindow(FrontWindow());
  932.                 
  933.             else if (theItem == kQuitMenuItem)
  934.                 DoQuit();
  935.                 
  936.             break;
  937.  
  938.     }
  939.     HiliteMenu(0);      // unhilight menu title
  940. }
  941.  
  942.  
  943. /***************************************************
  944.  *  finally, the main program and event loop
  945.  ***************************************************/
  946.  
  947. main()
  948. {
  949.     OSErr        retCode;
  950.     long        gestResponse;
  951.     
  952.     EventRecord    mainEventRec;
  953.     Boolean        eventFlag;
  954.     Boolean        canRunFlag;
  955.     
  956.     WindowPtr    whichWindow;
  957.     Rect        tempRect;
  958.     
  959.     // initialize the toolbox
  960.     InitGraf(&qd.thePort);
  961.     InitFonts();
  962.     InitWindows();
  963.     InitMenus();
  964.     TEInit();
  965.     InitDialogs(nil);
  966.     InitCursor();
  967.     FlushEvents(everyEvent,0);
  968.     MaxApplZone();
  969.     
  970.     // can we run?  let's be optimistic
  971.     canRunFlag = true;
  972.     
  973.     // are the Apple Event and Drag managers available?
  974.     // they simply must be
  975.     
  976.     retCode = Gestalt(gestaltAppleEventsAttr, &gestResponse);
  977.     if (retCode != noErr ||
  978.         (gestResponse & (1 << gestaltAppleEventsPresent)) == 0)
  979.         
  980.         canRunFlag = false;
  981.  
  982.     retCode = Gestalt(gestaltDragMgrAttr, &gestResponse);
  983.     if (retCode != noErr ||
  984.         (gestResponse & (1 << gestaltDragMgrPresent)) == 0)
  985.         
  986.         canRunFlag = false;
  987.  
  988.     if (!canRunFlag) ExitToShell();  // an alert would be nicer
  989.     
  990.     
  991.     // use Gestalt to find out what the world is like
  992.     // in particular, check for Color QuickDraw
  993.     
  994.     retCode = Gestalt(gestaltQuickdrawVersion, &gestResponse);
  995.     if (retCode != noErr || gestResponse < 0x0100)
  996.         gHasColorQuickdrawFlag = false;
  997.     else
  998.         gHasColorQuickdrawFlag = true;
  999.  
  1000.  
  1001.     // now let's initialize everything and install the
  1002.     // drag and Apple event handlers
  1003.  
  1004.     // initialize application globals
  1005.     
  1006.     gQuitFlag = false;
  1007.     gWindowCounter = 0;
  1008.     
  1009.     // install Apple event handlers
  1010.     (void) InstallAppleEventHandlers();
  1011.     
  1012.     // make the menus
  1013.     CreateMenus();
  1014.         
  1015.     // main event loop
  1016.     
  1017.     while (!gQuitFlag) {
  1018.  
  1019.         eventFlag = WaitNextEvent(everyEvent, &mainEventRec, 60*60*60, nil);
  1020.         
  1021.         switch(mainEventRec.what) {
  1022.         
  1023.             case mouseDown:
  1024.                 switch (FindWindow(mainEventRec.where, &whichWindow)) {
  1025.     
  1026.                     case inSysWindow:  // desk accessory window
  1027.                         SystemClick(&mainEventRec, whichWindow);
  1028.                         break;
  1029.                         
  1030.                     case inMenuBar:
  1031.                         DoMenuCommand(MenuSelect(mainEventRec.where));
  1032.                         break;
  1033.                         
  1034.                     case inDrag:
  1035.                         tempRect = qd.screenBits.bounds;
  1036.                         DragWindow(whichWindow, mainEventRec.where, &tempRect);
  1037.                         break;
  1038.                         
  1039.                     case inGoAway:
  1040.                         if (TrackGoAway(whichWindow, mainEventRec.where))
  1041.                             DoCloseWindow(whichWindow);
  1042.                         break;
  1043.                         
  1044.                     case inContent:
  1045.                     
  1046.                         // check to see if the user is starting a drag
  1047.                         if (WaitMouseMoved(mainEventRec.where))
  1048.                             
  1049.                             // if so, do the drag magic
  1050.                             DoWindowContentDrag(whichWindow, &mainEventRec);
  1051.                             
  1052.                         else if (whichWindow != FrontWindow())
  1053.                             SelectWindow(whichWindow);
  1054.                         break;
  1055.                 }
  1056.                 break;
  1057.  
  1058.             case updateEvt:
  1059.                 whichWindow = (WindowPtr) mainEventRec.message;
  1060.                 
  1061.                 SetPort(whichWindow);
  1062.                 BeginUpdate(whichWindow);
  1063.                 
  1064.                 EraseRect(&whichWindow->portRect);
  1065.                 DrawWindow(whichWindow);
  1066.                 
  1067.                 EndUpdate(whichWindow);
  1068.                 break;
  1069.                         
  1070.             case keyDown:
  1071.             case autoKey:
  1072.                 if (mainEventRec.modifiers & cmdKey)
  1073.                     DoMenuCommand(MenuKey(mainEventRec.message & charCodeMask));
  1074.                 break;
  1075.                 
  1076.             case kHighLevelEvent:
  1077.                 DoHighLevelEvent(&mainEventRec);
  1078.                 break;
  1079.             
  1080.             // a real app should handle all of these events.
  1081.             // Since the code is in Inside Mac:Mac Toolbox Essentials,
  1082.             // you really have no excuse for not supporting all the
  1083.             // standard low-level events
  1084.             
  1085.             
  1086.             
  1087.             // however, this is just a sample program, so I have an excuse
  1088.             
  1089.             case activateEvt:
  1090.             case osEvt:
  1091.             case diskEvt:
  1092.             case mouseUp:
  1093.                 break;
  1094.         }
  1095.     }
  1096.     
  1097.     // Good night
  1098. }
  1099.